﻿Public NotInheritable Class UndoRedoEngine

    Private Shared _Instance As UndoRedoEngine = New UndoRedoEngine()

    Public Shared ReadOnly Property Instance As UndoRedoEngine
        Get
            Return _Instance
        End Get
    End Property

    Public Property ActiveContext As Object
        Get
            Return _ActiveContext
        End Get
        Set(ByVal value As Object)

            If value Is _ActiveContext Then Return

            _ActiveContext = value

            If _ActiveContext Is Nothing Then

                ModuleDashboarding._dashboardmenuitem.Children(1).Title = "Annuler"
                ModuleDashboarding._dashboardmenuitem.Children(2).Title = "Retablir"

            Else

                If Not _TokenGroupUndoQueue.ContainsKey(_ActiveContext) Then
                    ModuleDashboarding._dashboardmenuitem.Children(2).Title = "Retablir"
                Else
                    If _TokenGroupUndoQueue(_ActiveContext).Count = 0 Then
                        ModuleDashboarding._dashboardmenuitem.Children(2).Title = "Retablir"
                    Else
                        ModuleDashboarding._dashboardmenuitem.Children(2).Title = "Retablir : " & _TokenGroupUndoQueue(_ActiveContext).Peek().Message
                    End If
                End If

                If Not _TokenGroupQueue.ContainsKey(_ActiveContext) Then
                    ModuleDashboarding._dashboardmenuitem.Children(1).Title = "Annuler"
                Else
                    If _TokenGroupQueue(_ActiveContext).Count = 0 Then
                        ModuleDashboarding._dashboardmenuitem.Children(1).Title = "Annuler"
                    Else
                        ModuleDashboarding._dashboardmenuitem.Children(1).Title = "Annuler : " & _TokenGroupQueue(_ActiveContext).Peek().Message
                    End If
                End If

            End If

        End Set
    End Property


    Public Sub OpenGroup(ByVal Message As String)
        If _ActiveContext Is Nothing Then Return

        Dim newgroup = New TokenGroup()
        newgroup.Message = Message
        newgroup.Context = ActiveContext
        _PreviousGroup = _ActiveGroup
        _ActiveGroup = newgroup
    End Sub
    Public Sub Push(
        ByVal UndoAction As Action(Of Object), _
        ByVal RedoAction As Action(Of Object), _
        ByVal Message As String)

        If _ActiveContext Is Nothing Then Return
        If _ActiveGroup Is Nothing Then Return
        If _ActiveContext IsNot _ActiveGroup.Context Then Return

        Dim newtoken = New Token()
        newtoken.Message = Message
        newtoken.UndoAction = UndoAction
        newtoken.RedoAction = RedoAction
        _ActiveGroup.Add(newtoken)
    End Sub
    Public Sub CancelGroup()
        If _ActiveContext Is Nothing Then Return
        If _ActiveGroup Is Nothing Then Return
        If _ActiveContext IsNot _ActiveGroup.Context Then Return

        _ActiveGroup = _PreviousGroup
    End Sub
    Public Sub CloseGroup()
        If _ActiveContext Is Nothing Then Return
        If _ActiveGroup Is Nothing Then Return
        If _ActiveContext IsNot _ActiveGroup.Context Then Return

        If Not _TokenGroupQueue.ContainsKey(ActiveContext) Then
            _TokenGroupQueue.Add(ActiveContext, New Stack(Of TokenGroup)())
        End If

        _TokenGroupQueue(ActiveContext).Push(_ActiveGroup)

        If _TokenGroupUndoQueue.ContainsKey(_ActiveContext) Then
            _TokenGroupUndoQueue(_ActiveContext).Clear()
        End If


        ModuleDashboarding._dashboardmenuitem.Children(1).Title = "Annuler : " & _ActiveGroup.Message

        ModuleDashboarding._dashboardmenuitem.Children(2).Title = "Retablir"

        _ActiveGroup = _PreviousGroup
    End Sub

    Private _ActiveContext As Object
    Private _ActiveGroup As TokenGroup
    Private _PreviousGroup As TokenGroup
    Private _TokenGroupQueue As New Dictionary(Of Object, Stack(Of TokenGroup))
    Private _TokenGroupUndoQueue As New Dictionary(Of Object, Stack(Of TokenGroup))
    'Private _TokenGroupRedoQueue As New Dictionary(Of Object, Stack(Of TokenGroup))

    Friend Sub Undo()
        If _ActiveContext Is Nothing Then Return
        If Not _TokenGroupQueue.ContainsKey(ActiveContext) Then Return
        If _TokenGroupQueue(_ActiveContext).Count = 0 Then Return

        If Not _TokenGroupUndoQueue.ContainsKey(ActiveContext) Then
            _TokenGroupUndoQueue.Add(ActiveContext, New Stack(Of TokenGroup)())
        End If

        _TokenGroupUndoQueue(_ActiveContext).Push(_TokenGroupQueue(_ActiveContext).Pop())

        ModuleDashboarding._dashboardmenuitem.Children(2).Title = "Retablir : " & _TokenGroupUndoQueue(_ActiveContext).Peek().Message

        If _TokenGroupQueue(_ActiveContext).Count = 0 Then
            ModuleDashboarding._dashboardmenuitem.Children(1).Title = "Annuler"
        Else
            ModuleDashboarding._dashboardmenuitem.Children(1).Title = "Annuler : " & _TokenGroupQueue(_ActiveContext).Peek().Message
        End If


        _TokenGroupUndoQueue(_ActiveContext).Peek().Undo()
    End Sub
    Friend Sub Redo()
        If _ActiveContext Is Nothing Then Return
        If Not _TokenGroupUndoQueue.ContainsKey(ActiveContext) Then Return
        If Not _TokenGroupQueue.ContainsKey(ActiveContext) Then Return
        If _TokenGroupUndoQueue(_ActiveContext).Count = 0 Then Return

        _TokenGroupQueue(_ActiveContext).Push(_TokenGroupUndoQueue(_ActiveContext).Pop())

        ModuleDashboarding._dashboardmenuitem.Children(1).Title = "Annuler : " & _TokenGroupQueue(_ActiveContext).Peek().Message

        If _TokenGroupUndoQueue(_ActiveContext).Count = 0 Then
            ModuleDashboarding._dashboardmenuitem.Children(2).Title = "Retablir"
        Else
            ModuleDashboarding._dashboardmenuitem.Children(2).Title = "Retablir : " & _TokenGroupUndoQueue(_ActiveContext).Peek().Message
        End If

        _TokenGroupQueue(_ActiveContext).Peek().Redo()
    End Sub

    Private Class Token
        Public UndoAction As Action(Of Object)
        Public RedoAction As Action(Of Object)
        Public Message As String
    End Class

    Private Class TokenGroup
        Inherits List(Of Token)

        Public Message As String
        Public Context As Object

        Public Sub Undo()
            For i = Me.Count - 1 To 0 Step -1
                Me(i).UndoAction(Me.Context)
            Next
        End Sub


        Public Sub Redo()
            For i = Me.Count - 1 To 0 Step -1
                Me(i).RedoAction(Me.Context)
            Next
        End Sub
    End Class
End Class